Now, groups can retrieve a list of the contacts in that group. I probably should...
[adiumx.git] / Frameworks / Adium Framework / Source / AIListGroup.m
blob6edae072f2505b56c3e240f8e1fa6df1c3a5ae37
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import <Adium/AIContactControllerProtocol.h>
18 #import <Adium/AIListGroup.h>
19 #import <Adium/AISortController.h>
20 #import <AIUtilities/AIArrayAdditions.h>
22 @interface AIListGroup (PRIVATE)
23 - (void)_recomputeVisibleCount;
24 @end
26 @implementation AIListGroup
28 //init
29 - (id)initWithUID:(NSString *)inUID
31     if ((self = [super initWithUID:inUID service:nil])) {
32                 containedObjects = [[NSMutableArray alloc] init];
33                 expanded = YES;
35                 largestOrder = 1.0;
36                 smallestOrder = 1.0;
38                 //Default invisible
39                 visibleCount = 0;
40                 visible = NO;
41     }
42         
43     return self;
46 - (void)dealloc
48         [containedObjects release]; containedObjects = nil;
49         
50         [super dealloc];
53 /* An object ID generated by Adium that is shared by all objects which are, to most intents and purposes, identical to
54  * this object.  Ths ID is composed of the service ID and UID, so any object with identical services and object ID's
55  * will have the same value here.
56  */
57 - (NSString *)internalObjectID
59         if (!internalObjectID) {
60                 internalObjectID = [[AIListObject internalObjectIDForServiceID:@"Group" UID:[self UID]] retain];
61         }
62         return internalObjectID;
65 /*!
66  * @brief Generate a special identifier for this group based upon its contents
67  *
68  * This is useful for storing preferences which are related not to the name of this group (which might be arbitrary) but
69  * rather to its contents. The contact list root always returns its own UID, but other groups will have a different 
70  * contentsBasedIdentifier depending upon what other objects they contain.
71  */
72 - (NSString *)contentsBasedIdentifier
74         NSString *contentsBasedIdentifier;
75         if (self == [[adium contactController] contactList]) {
76                 contentsBasedIdentifier = [self UID];
78         } else {                
79                 NSArray *UIDArray = [[containedObjects valueForKey:@"UID"] sortedArrayUsingSelector:@selector(compare:)];
80                 contentsBasedIdentifier = [UIDArray componentsJoinedByString:@";"];
81                 if (![contentsBasedIdentifier length]) contentsBasedIdentifier = [self UID];
82         }
83         
84         return contentsBasedIdentifier;
87 //Visibility -----------------------------------------------------------------------------------------------------------
88 #pragma mark Visibility
90  The visible objects contained in a group are always sorted to the top.  This allows us to easily retrieve only visible
91  objects without having to physically remove invisible objects from the group.
92  */
93 //Returns the number of visible objects in this group
94 - (unsigned)visibleCount
96     return visibleCount;
99 //Cache the number of contained objects that are visible
100 - (void)_recomputeVisibleCount
102         visibleCount = 0;
103         
104         NSEnumerator *containedObjectEnumerator = [[self containedObjects] objectEnumerator];
105         AIListObject *containedObject = nil;
106         
107         while ((containedObject = [containedObjectEnumerator nextObject])){
108                 if ([containedObject visible]){
109                         visibleCount++;
110                 } else if ([containedObject isKindOfClass:[AIListGroup class]] && 
111                                    ([[adium contactController] isGroupDetached:self] || [containedObject alwaysVisible])) {
112                         visibleCount++;
113                 }
114         }
115         
116         [self setStatusObject:(visibleCount ? [NSNumber numberWithInt:visibleCount] : nil)
117                                    forKey:@"VisibleObjectCount"
118                                    notify:NotifyNow];
121 //Called when the visibility of an object in this group changes
122 - (void)visibilityOfContainedObject:(AIListObject *)inObject changedTo:(BOOL)inVisible
124         //Update our visibility as a result of this change
125         [self _recomputeVisibleCount];
126         
127         //Sort the contained object to or from the bottom (invisible section) of the group
128         [[adium contactController] sortListObject:inObject];
131 //Object Storage ---------------------------------------------------------------------------------------------
132 #pragma mark Object Storage
133 //Return our contained objects
134 - (NSArray *)containedObjects
136         return containedObjects;
139 //Number of containd objects
140 - (unsigned)containedObjectsCount
142     return [containedObjects count];
145 //Test for the presence of an object in our group
146 - (BOOL)containsObject:(AIListObject *)inObject
148         return [containedObjects containsObject:inObject];
151 - (BOOL)canContainOtherContacts {
152     return NO;
155 - (BOOL)containsMultipleContacts {
156     return NO;
159 //Retrieve an object by index
160 - (id)objectAtIndex:(unsigned)index
162     return [containedObjects objectAtIndex:index];
165 //Retrieve the index of an object
166 - (int)indexOfObject:(AIListObject *)inObject
168     return [containedObjects indexOfObject:inObject];
171 - (NSArray *)listContacts
173         return containedObjects;
176 //Remove all the objects from this group (PRIVATE: For contact controller only)
177 - (void)removeAllObjects
179         //Remove all the objects
180         while ([containedObjects count]) {
181                 [self removeObject:[containedObjects objectAtIndex:0]];
182         }
185 //Retrieve a specific object by service and UID
186 - (AIListObject *)objectWithService:(AIService *)inService UID:(NSString *)inUID
188         NSEnumerator    *enumerator = [containedObjects objectEnumerator];
189         AIListObject    *object;
190         
191         while ((object = [enumerator nextObject])) {
192                 if ([inUID isEqualToString:[object UID]] && [object service] == inService) break;
193         }
194         
195         return object;
199  * @brief Add an object to this group
201  * PRIVATE: For contact controller only. Sorting and visible count updating will be performed as needed.
203  * @result YES if the object was added (that is, was not already present)
204  */
205 - (BOOL)addObject:(AIListObject *)inObject
207         BOOL success = NO;
208         
209         if (![containedObjects containsObjectIdenticalTo:inObject]) {
210                 //Add the object
211                 [inObject setContainingObject:self];
212                 [containedObjects addObject:inObject];
213                 
214                 //Update our visible count
215                 [self _recomputeVisibleCount];
216                 
217                 /* Sort this object on our own.  This always comes along with a content change, so calling contact controller's
218                  * sort code would invoke an extra update that we don't need.  We can skip sorting if this object is not visible,
219                  * since it will add to the bottom/non-visible section of our array.
220                  */
221                 if ([inObject visible]) {
222                         [self sortListObject:inObject
223                                   sortController:[[adium contactController] activeSortController]];
224                 }
225                 
226                 //
227                 [self setStatusObject:[NSNumber numberWithInt:[containedObjects count]] 
228                                            forKey:@"ObjectCount"
229                                            notify:NotifyNow];
230                 
231                 success = YES;
232         }
233         
234         return success;
237 //Remove an object from this group (PRIVATE: For contact controller only)
238 - (void)removeObject:(AIListObject *)inObject
239 {       
240         if ([containedObjects containsObject:inObject]) {               
241                 //Remove the object
242                 [inObject setContainingObject:nil];
243                 [containedObjects removeObject:inObject];
244                 
245                 //Update our visible count
247                 [self _recomputeVisibleCount];
249                 //
250                 [self setStatusObject:[NSNumber numberWithInt:[containedObjects count]]
251                                            forKey:@"ObjectCount" 
252                                            notify:NotifyNow];
253         }
256 //Move group from one contact list to another
257 - (BOOL)moveGroupTo:(AIListObject<AIContainingObject> *)list
259         return [self moveGroupFrom:[self containingObject] to:list];
262 - (BOOL)moveGroupFrom:(AIListObject<AIContainingObject> *)fromList to:(AIListObject<AIContainingObject> *)toList
264         // Check if group is not already there
265         if([toList containsObject:self])
266                 return NO;
267         
268         [fromList removeObject:self];
269         [toList addObject:self];
270         [self setContainingObject:toList];
271         
272         return YES;
275 - (BOOL)moveAllGroupsFrom:(AIListGroup *)fromContactList to:(AIListGroup *)toContactList {
276         NSEnumerator *groups = [containedObjects objectEnumerator];
277         AIListGroup *group;
278         
279         while ((group = [groups nextObject]))
280                 [group moveGroupTo:toContactList];
281         
282         return YES;
285 //Sorting --------------------------------------------------------------------------------------------------------------
286 #pragma mark Sorting
287 //Resort an object in this group (PRIVATE: For contact controller only)
288 - (void)sortListObject:(AIListObject *)inObject sortController:(AISortController *)sortController
290         [inObject retain];
291         [containedObjects removeObject:inObject];
292         [containedObjects insertObject:inObject 
293                                                    atIndex:[sortController indexForInserting:inObject 
294                                                                                                                  intoObjects:containedObjects]];
295         [inObject release];
298 //Resorts the group contents (PRIVATE: For contact controller only)
299 - (void)sortGroupAndSubGroups:(BOOL)subGroups sortController:(AISortController *)sortController
301     //Sort the groups within this group
302     if (subGroups) {
303                 NSEnumerator            *enumerator;
304                 AIListObject            *object;
305                 
306         enumerator = [containedObjects objectEnumerator];
307         while ((object = [enumerator nextObject])) {
308             if ([object isMemberOfClass:[AIListGroup class]]) {
309                 [(AIListGroup *)object sortGroupAndSubGroups:YES
310                                                                                           sortController:sortController];
311             }
312         }
313     }
314         
315     //Sort this group
316     if (sortController) {
317                 NSMutableArray  *sortedListObjects;
318                 
319                 if ([containedObjects count] > 1) {
320                         sortedListObjects = [[sortController sortListObjects:containedObjects] mutableCopy];
321                         [containedObjects release]; containedObjects = sortedListObjects;
322                 }
323     }
327 //Expanded State -------------------------------------------------------------------------------------------------------
328 #pragma mark Expanded State
329 //Set the expanded/collapsed state of this group (PRIVATE: For the contact list view to let us know our state)
330 - (void)setExpanded:(BOOL)inExpanded
332     expanded = inExpanded;
333         loadedExpanded = YES;
335 //Returns the current expanded/collapsed state of this group
336 - (BOOL)isExpanded
338         if (!loadedExpanded) {
339                 loadedExpanded = YES;
340                 expanded = [[self preferenceForKey:@"IsExpanded"
341                                                                          group:@"Contact List"] boolValue];
342         }
344     return expanded;
347 - (BOOL)isExpandable
349         return YES;
352 //Order index
353 - (void)listObject:(AIListObject *)listObject didSetOrderIndex:(float)inOrderIndex
355         if (inOrderIndex > largestOrder) {
356                 largestOrder = inOrderIndex;
357         } else if (inOrderIndex < smallestOrder) {
358                 smallestOrder = inOrderIndex;
359         }
362 - (float)smallestOrder
364         return smallestOrder;
367 - (float)largestOrder
369         return largestOrder;
372 #pragma mark Applescript
373 - (NSScriptObjectSpecifier *)objectSpecifier
375         NSScriptClassDescription *containerClassDesc = (NSScriptClassDescription *)[NSScriptClassDescription classDescriptionForClass:[NSApp class]];
376         return [[[NSNameSpecifier alloc]
377                    initWithContainerClassDescription:containerClassDesc
378                    containerSpecifier:nil key:@"contactGroups"
379                    name:[self UID]] autorelease];
382 - (NSArray *)contacts
384         return [self containedObjects];
387 @end